from flask_restful import Resource
from flask import current_app
from flask import request
from flask_restful.reqparse import RequestParser
from flask_restful import inputs

from datetime import datetime

from models.user import User
from toutiao import db
from toutiao.utils.token import get_user_token
from toutiao.utils.parsers import check_mobile
from toutiao.utils.decorators import Loginrequired
from cache.user import UserProfileCache
from flask import g


class SmsCodeResource(Resource):

    '''
    (以下步骤都需程序员自己分析）
    需求：用户输入手机号，点击获取验证码，后端发送验证码（荣联云，阿里云）
    步骤：
        获取手机号
        生产验证码
        保存验证码
        返回响应
    url:(先init中绑定路由，views中写视图，也可写在别处，注意导包路径和方式)
        请求方式：GET /app/v1_0/sms/codes/<mobile>/
        后台中其他请求路径均不以/结尾
    '''

    def get(self,mobile):
        #1.获取mobile
        #2.随机生成6位数，根据产品需求决定
        from random import choice #随机获取一个数值
        from string import digits #0-9的整数
        code = ''.join([choice(digits) for _ in range(6)])
        #3.保存验证码到数据库
        current_app.redis_store.setex('app:code:{}'.format(mobile),60*60,code) #key,expire,value
        #日志中打印code信息，方便后台程序员进行验证登录
        current_app.logger.info(code)

        #4.返回响应
        return {
            'mobile':mobile
        }


#redis数据库中set app:code:18888888888 888888无过期时间，方便后台程序员进行验证登录
class LoginResource(Resource):
    '''
    手机号登录产品设计原型：可参考其他网站学习
        1、手机号验证
        2、三方登录方式为：登录三方账户，新注册账号绑定。
        3、手机号验证11位数字
        4、验证码有效期一分钟，过期后可重新发送
        5、验证失败3次后进行滑块验证,前端实现
        6、未注册用户直接注册登录

    需求分析：
        用户输入手机号和短信验证码之后，点击登录
        前端将手机号和验证码以以axios的请求，发送json给后端
    步骤：
        1.接受数据
        2.验证数据
            验证短信验证码,手机号验证11位数字
            根据手机号判断是否注册①已注册②未注册
        3.实现核心逻辑
            生成token
        4.返回响应
                    {
              "message": "ok",
              "data": {
                "token": "xxxxxxxx"
              }
            }
    请求方式：
        POST /app/v1_0/authorizations
    '''

    def post(self):
        #接收数据
        data = request.json
        #验证数据格式
        parser = RequestParser()
        parser.add_argument('mobile',         #参数名
                            location='json',  #location 即args,json,form,cookies,headers
                            required=True,
                            # type=inputs.int_range(13000000000,19999999999))
                            type=check_mobile)   #手机验证方法抽取到utils
        parser.add_argument('code',location='json',
                            # requires=True,
                            # type=inputs.int_range(100000,999999),
                            type=inputs.regex('\d{6}')
                            )
        args = parser.parse_args()
        mobile = args['mobile']
        code = args.get('code')
        # 1.前端发送字符串类型，需用正则校验(本案例前端组件中发送为字符串)
        # 2.postman测试时，如若mobile和code前端发送int类型，需用inputs.int_range校验，
        #   此时需code=str(code)，与数据库查询出的code比较时，需统一类型

        #验证验证码
        #此处format需传入字符串，redis查询到的数据为b'455788'字节类型字符串
        redis_code =current_app.redis_store.get('app:code:{}'.format(mobile))

        if redis_code is None or redis_code.decode() != code:
            return {'message':'短信验证码错误'},400
        #判断用户是否注册
        user = None
        try:
            user = User.query.filter_by(mobile=mobile).first()
        except Exception as e:
            current_app.logger.error(e)

        if user is not None:
            #如果注册过，修改用户表最后登录时间
            user.last_login= datetime.now()
            try:
                db.session.commit()
            except Exception as e:
                current_app.logger(e)
                db.session.rollback()

        else:
            #如果没有注册过，保存数据注册用户
            # (user_basic表name和mobile为unique唯一，所以必传)
            user_id = current_app.id_worker.get_id()
            user=User()
            user.id = user_id
            user.mobile = mobile
            user.name = mobile
            user.last_login = datetime.now()
            #保存数据库的代码都try一下
            try:
                db.session.add(user)
                db.session.commit()
            except Exception as e:
                current_app.logger.error(e)
                db.session.rollback()
                return {'message':'dababase error'},507 #存储问题

        #生成tocken(方法抽取到utils)
        #使用isdangerous时返回一个token,现在改为使用jwt，返回两个token
        token,refresh_token = get_user_token(user.id)

        #缓存用户信息 user:{user_id}:profile  string  (哈希字典比较麻烦，选取字符串，字符串为要存储内容的字典)
        from cache.user import UserProfileCache,UserStatusCache
        UserProfileCache(user.id).save()
        # 缓存用户状态
        UserStatusCache(user.id).save(user.status)

        return {'token':token,
                'refresh_token':refresh_token}

    #刷新token
    def put(self):
        """
           根据refresh_token 来换取token
           不会走我们的 loginrequired装饰器

           1. user_id 和 refresh 都要有才正确
           2. 最好先判断一下 用户的状态
           3. 正确的话 就重写生成一个token
           4. user_id 和 refresh 有一个不正确 都返回 403 ，让前端登陆
           :return:
        """
        if g.user_id and g.is_refresh_token:
            #用户状态
            from cache.user import UserStatusCache
            if not UserStatusCache(g.user_id).get():
                return {'message':'login'},403
            #允许用户登录时刷新token
            from toutiao.utils.token import get_user_token
            token,refresh_token = get_user_token(g.user_id)
            return {'token':token}

        else:
            return {'message':'login'},403


#获取用户信息(调用缓存类获取)
class UserInfoResource(Resource):

    #只有登录用户可以获取到
    method_decorators = [Loginrequired]

    def get(self):
        user_id = g.user_id
        user_dict = UserProfileCache(user_id).get()

        return user_dict



#以下为使用ab测试有缓存和无缓存处理并发速度的两个接口,使用ab见remarks
#查询数据
class UserInfo(Resource):

    def get(self):
        user = User.query.filter_by(id=1).first()

        user_data = {
            'id': user.id,
            'mobile': user.mobile,
            'name': user.name,
            'photo': user.profile_photo or '',
            'is_media': user.is_media,
            'intro': user.introduction or '',
            'certi': user.certificate or '',
        }

        return user_data

import json
#查询redis缓存
class CacheUserInfo(Resource):

    def get(self):
        redis_data=current_app.redis_store.get(1)
        user_data=json.loads(redis_data.decode())
        return user_data
    #用户第一次将数据库中的数据保存到缓存
    def post(self):
        user = User.query.filter_by(id=1).first()

        user_data = {
            'id': user.id,
            'mobile': user.mobile,
            'name': user.name,
            'photo': user.profile_photo or '',
            'is_media': user.is_media,
            'intro': user.introduction or '',
            'certi': user.certificate or '',
        }

        current_app.redis_store.set(1, json.dumps(user_data))

        return {'message':'ok'}


